diff options
Diffstat (limited to 'app/[lng]/evcp/(evcp)/menu-access-dept/_components/department-domain-assignment-dialog.tsx')
| -rw-r--r-- | app/[lng]/evcp/(evcp)/menu-access-dept/_components/department-domain-assignment-dialog.tsx | 380 |
1 files changed, 0 insertions, 380 deletions
diff --git a/app/[lng]/evcp/(evcp)/menu-access-dept/_components/department-domain-assignment-dialog.tsx b/app/[lng]/evcp/(evcp)/menu-access-dept/_components/department-domain-assignment-dialog.tsx deleted file mode 100644 index f8a75641..00000000 --- a/app/[lng]/evcp/(evcp)/menu-access-dept/_components/department-domain-assignment-dialog.tsx +++ /dev/null @@ -1,380 +0,0 @@ -"use client"; - -import * as React from "react"; -import { Loader2, Users, Building2, AlertCircle } from "lucide-react"; -import { Button } from "@/components/ui/button"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, -} from "@/components/ui/dialog"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; -import { Textarea } from "@/components/ui/textarea"; -import { Badge } from "@/components/ui/badge"; -import { Label } from "@/components/ui/label"; -import { Separator } from "@/components/ui/separator"; -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from "@/components/ui/table"; -import { - DepartmentNode -} from "@/lib/users/knox-service"; -import { - getDepartmentDomainAssignmentsByDepartments -} from "@/lib/users/department-domain/service"; -import { DOMAIN_OPTIONS, getDomainLabel } from "./domain-constants"; - -interface ExistingAssignment { - id: number; - companyCode: string; - departmentCode: string; - departmentName: string; - assignedDomain: string; - description?: string | null; - createdAt: Date; - updatedAt: Date; -} - -interface DepartmentDomainAssignmentDialogProps { - open: boolean; - onOpenChange: (open: boolean) => void; - selectedDepartments: string[]; - departments: DepartmentNode[]; - companyInfo: { code: string; name: string }; - onAssign: (assignments: { - departmentCodes: string[]; - domain: string; - description?: string; - }) => Promise<void>; - isLoading?: boolean; -} - -export function DepartmentDomainAssignmentDialog({ - open, - onOpenChange, - selectedDepartments, - departments, - companyInfo, - onAssign, - isLoading = false, -}: DepartmentDomainAssignmentDialogProps) { - const [selectedDomain, setSelectedDomain] = React.useState<string>(""); - const [description, setDescription] = React.useState<string>(""); - const [isSubmitting, setIsSubmitting] = React.useState(false); - const [existingAssignments, setExistingAssignments] = React.useState<ExistingAssignment[]>([]); - const [isLoadingAssignments, setIsLoadingAssignments] = React.useState(false); - - // 선택된 부서들의 정보 가져오기 - const getSelectedDepartmentInfo = React.useCallback(() => { - const findDepartment = (nodes: DepartmentNode[], code: string): DepartmentNode | null => { - for (const node of nodes) { - if (node.departmentCode === code) { - return node; - } - const found = findDepartment(node.children, code); - if (found) return found; - } - return null; - }; - - return selectedDepartments - .map(code => findDepartment(departments, code)) - .filter(Boolean) as DepartmentNode[]; - }, [departments, selectedDepartments]); - - // 회사별로 그룹화 - const selectedDepartmentsByCompany = React.useMemo(() => { - const deptInfo = getSelectedDepartmentInfo(); - const grouped = new Map<string, DepartmentNode[]>(); - - deptInfo.forEach(dept => { - if (!grouped.has(dept.companyCode)) { - grouped.set(dept.companyCode, []); - } - grouped.get(dept.companyCode)!.push(dept); - }); - - return grouped; - }, [getSelectedDepartmentInfo]); - - // 기존 할당 정보 조회 - React.useEffect(() => { - if (open && selectedDepartments.length > 0) { - const loadExistingAssignments = async () => { - setIsLoadingAssignments(true); - try { - const assignments = await getDepartmentDomainAssignmentsByDepartments(selectedDepartments); - setExistingAssignments(assignments as ExistingAssignment[]); - } catch (error) { - console.error("기존 할당 정보 조회 실패:", error); - setExistingAssignments([]); - } finally { - setIsLoadingAssignments(false); - } - }; - - loadExistingAssignments(); - } else { - setExistingAssignments([]); - } - }, [open, selectedDepartments]); - - // 폼 초기화 - React.useEffect(() => { - if (open) { - setSelectedDomain(""); - setDescription(""); - setIsSubmitting(false); - } - }, [open]); - - // 할당 처리 - const handleAssign = async () => { - if (!selectedDomain || selectedDepartments.length === 0) { - return; - } - - setIsSubmitting(true); - - try { - await onAssign({ - departmentCodes: selectedDepartments, - domain: selectedDomain, - description: description.trim() || undefined, - }); - - // 성공 시 다이얼로그 닫기 - onOpenChange(false); - } catch (error) { - console.error("도메인 할당 실패:", error); - } finally { - setIsSubmitting(false); - } - }; - - const canSubmit = selectedDomain && selectedDepartments.length > 0 && !isSubmitting && !isLoading; - const selectedDomainInfo = DOMAIN_OPTIONS.find(opt => opt.value === selectedDomain); - const hasConflicts = existingAssignments.some(a => a.assignedDomain !== selectedDomain && selectedDomain); - - return ( - <Dialog open={open} onOpenChange={onOpenChange}> - <DialogContent className="max-w-4xl max-h-[90vh] overflow-hidden flex flex-col"> - <DialogHeader> - <DialogTitle className="flex items-center gap-2"> - <Building2 className="h-5 w-5" /> - 부서별 도메인 할당 - </DialogTitle> - <DialogDescription> - 선택된 {selectedDepartments.length}개 부서에 도메인을 할당합니다. - 상위 부서를 선택한 경우 하위 부서들도 자동으로 포함됩니다. - </DialogDescription> - </DialogHeader> - - <div className="flex-1 overflow-y-auto px-1"> - <div className="space-y-6 pr-3"> - {/* 선택된 부서들 표시 */} - <div className="space-y-3"> - <Label className="text-sm font-medium flex items-center gap-2"> - <Users className="h-4 w-4" /> - 선택된 부서 ({selectedDepartments.length}개) - </Label> - - <div className="border rounded-md p-3 max-h-40 overflow-y-auto"> - {Array.from(selectedDepartmentsByCompany.entries()).map(([companyCode, depts]) => ( - <div key={companyCode} className="mb-3 last:mb-0"> - <div className="text-sm font-medium text-muted-foreground mb-2"> - {companyCode} - {companyInfo.name} - </div> - <div className="flex flex-wrap gap-2"> - {depts.map((dept) => ( - <Badge - key={dept.departmentCode} - variant="outline" - className="text-xs" - > - {dept.departmentName || dept.departmentCode} - </Badge> - ))} - </div> - </div> - ))} - </div> - </div> - - {/* 기존 할당 현황 */} - {(existingAssignments.length > 0 || isLoadingAssignments) && ( - <> - <Separator /> - <div className="space-y-3"> - <Label className="text-sm font-medium flex items-center gap-2"> - <AlertCircle className="h-4 w-4" /> - 현재 할당 현황 - </Label> - - {isLoadingAssignments ? ( - <div className="flex items-center justify-center py-4"> - <Loader2 className="h-4 w-4 animate-spin mr-2" /> - 기존 할당 정보를 조회하는 중... - </div> - ) : ( - <div className="border rounded-md max-h-60 overflow-y-auto"> - <Table> - <TableHeader> - <TableRow> - <TableHead>부서</TableHead> - <TableHead>현재 도메인</TableHead> - <TableHead>할당일</TableHead> - <TableHead>설명</TableHead> - </TableRow> - </TableHeader> - <TableBody> - {existingAssignments.map((assignment) => ( - <TableRow key={assignment.id}> - <TableCell className="font-medium"> - {assignment.departmentName} - </TableCell> - <TableCell> - <Badge - variant={assignment.assignedDomain === 'evcp' ? 'default' : 'secondary'} - > - {getDomainLabel(assignment.assignedDomain)} - </Badge> - </TableCell> - <TableCell className="text-sm text-muted-foreground"> - {new Date(assignment.createdAt).toLocaleDateString('ko-KR')} - </TableCell> - <TableCell className="max-w-xs truncate text-sm"> - {assignment.description || '-'} - </TableCell> - </TableRow> - ))} - </TableBody> - </Table> - </div> - )} - - {hasConflicts && ( - <div className="bg-yellow-50 border-yellow-200 border rounded-md p-3"> - <div className="flex items-start gap-2"> - <AlertCircle className="h-4 w-4 text-yellow-600 mt-0.5" /> - <div className="text-sm"> - <div className="font-medium text-yellow-800">도메인 변경 주의</div> - <div className="text-yellow-700"> - 일부 부서의 기존 도메인과 다른 도메인을 할당하려고 합니다. - 기존 할당은 자동으로 비활성화됩니다. - </div> - </div> - </div> - </div> - )} - </div> - </> - )} - - <Separator /> - - {/* 도메인 선택 */} - <div className="space-y-2"> - <Label htmlFor="domain-select" className="text-sm font-medium"> - 할당할 도메인 * - </Label> - <Select value={selectedDomain} onValueChange={setSelectedDomain}> - <SelectTrigger id="domain-select"> - <SelectValue placeholder="도메인을 선택하세요" /> - </SelectTrigger> - <SelectContent> - {DOMAIN_OPTIONS.map((option) => ( - <SelectItem key={option.value} value={option.value}> - <div className="flex flex-col"> - <span className="font-medium">{option.label}</span> - <span className="text-xs text-muted-foreground"> - {option.description} - </span> - </div> - </SelectItem> - ))} - </SelectContent> - </Select> - - {selectedDomainInfo && ( - <div className="text-sm text-muted-foreground"> - <Badge variant="secondary" className="mr-2"> - {selectedDomainInfo.label} - </Badge> - {selectedDomainInfo.description} - </div> - )} - </div> - - {/* 할당 사유/설명 */} - <div className="space-y-2"> - <Label htmlFor="description" className="text-sm font-medium"> - 할당 사유 또는 설명 (선택사항) - </Label> - <Textarea - id="description" - placeholder="예: 구매 업무 담당자들에게 procurement 도메인 할당" - value={description} - onChange={(e) => setDescription(e.target.value)} - rows={3} - maxLength={500} - /> - <div className="text-xs text-muted-foreground text-right"> - {description.length}/500 - </div> - </div> - - {/* 주의사항 */} - <div className="bg-muted/50 p-3 rounded-md"> - <div className="text-sm text-muted-foreground"> - <div className="font-medium mb-1">⚠️ 주의사항</div> - <ul className="list-disc list-inside space-y-1 text-xs"> - <li>도메인 할당은 해당 부서 소속 사용자들의 메뉴 접근 권한에 영향을 줍니다.</li> - <li>기존에 다른 도메인이 할당된 부서는 새로운 도메인으로 덮어씌워집니다.</li> - <li>Knox 조직도 변경으로 인해 부서가 삭제된 경우, 해당 할당은 고립된 레코드가 됩니다.</li> - </ul> - </div> - </div> - </div> - </div> - - <DialogFooter className="border-t pt-4"> - <Button - variant="outline" - onClick={() => onOpenChange(false)} - disabled={isSubmitting || isLoading} - > - 취소 - </Button> - <Button - onClick={handleAssign} - disabled={!canSubmit} - > - {isSubmitting || isLoading ? ( - <> - <Loader2 className="mr-2 h-4 w-4 animate-spin" /> - 할당 중... - </> - ) : ( - `도메인 할당 (${selectedDepartments.length}개 부서)` - )} - </Button> - </DialogFooter> - </DialogContent> - </Dialog> - ); -}
\ No newline at end of file |
